bit = {
    data32 = {},
    data64 = {}
}

for i = 1, 32 do
    bit.data32[i] = 2 ^ (32 - i)
    bit.data64[i] = 2 ^ (64 - i)
end
 
function bit:d2b(arg)
    local tr = {}

    if arg > self.data64[32] then
        for i = 1, 32 do
            if arg >= self.data64[i] then
                arg = arg - self.data64[i]
                if arg < self.data64[32] then break end
            end
        end
    end

    for i = 1, 32 do
        if arg >= self.data32[i] then
            arg = arg - self.data32[i]
            tr[i] = 1
        else
            tr[i] = 0
        end
    end

    return tr
end

function bit:b2d(arg)
    local nr = 0
    for i = 1, 32 do
        if arg[i] == 1 then
            nr = nr + 2 ^ (32 - i)
        end
    end
    return nr
end

function bit:to32(arg)
    if arg >= self.data64[32] then
        for i = 1, 32 do
            if arg >= self.data64[i] then
                arg = arg - self.data64[i]
                if arg < self.data64[32] then break end
            end
        end
    end

    return arg
end

function bit:to8(arg)
    if arg >= self.data64[32] then
        for i = 1, 32 do
            if arg >= self.data64[i] then
                arg = arg - self.data64[i]
                if arg < self.data64[32] then break end
            end
        end
    end

    if arg >= self.data32[24] then
        for i = 1, 24 do
            if arg >= self.data32[i] then
                arg = arg - self.data32[i]
                if arg < self.data32[24] then break end
            end
        end
    end

    return arg
end

function bit:to4(arg)
    if arg >= self.data64[32] then
        for i = 1, 32 do
            if arg >= self.data64[i] then
                arg = arg - self.data64[i]
                if arg < self.data64[32] then break end
            end
        end
    end

    if arg >= self.data32[28] then
        for i = 1, 28 do
            if arg >= self.data32[i] then
                arg = arg - self.data32[i]
                if arg < self.data32[28] then break end
            end
        end
    end

    return arg
end

-- 异或
function bit:_xor(a,b)
    local op1 = self:d2b(a)
    local op2 = self:d2b(b)
    local r = {}
 
    for i = 1, 32 do
        if op1[i] == op2[i] then
            r[i] = 0
        else
            r[i] = 1
        end
    end
    return self:b2d(r)
end

-- 与
function bit:_and(a,b)
    local op1 = self:d2b(a)
    local op2 = self:d2b(b)
    local r = {}
    
    for i = 1, 32 do
        if op1[i] == 1 and op2[i] == 1 then
            r[i] = 1
        else
            r[i] = 0
        end
    end
    return self:b2d(r)
end

-- 或
function bit:_or(a,b)
    local op1 = self:d2b(a)
    local op2 = self:d2b(b)
    local r = {}
    
    for i = 1, 32 do
        if op1[i] == 1 or op2[i] == 1 then
            r[i] = 1
        else
            r[i] = 0
        end
    end

    return self:b2d(r)
end

-- 或 连续4次
function bit:_or4(a,b,c,d)
    local op1 = self:d2b(a)
    local op2 = self:d2b(b)
    local op3 = self:d2b(c)
    local op4 = self:d2b(d)
    local r = {}
    
    for i = 1, 32 do
        if op1[i] == 1 or op2[i] == 1 or op3[i] == 1 or op4[i] == 1 then
            r[i] = 1
        else
            r[i] = 0
        end
    end
    return self:b2d(r)
end

-- 左移
function bit:_lshift(a, n)
    return a * (self.data32[32 - n])
end
 
-- 右移
function bit:_rshift(a, n)
    return math.floor(a / (self.data32[32 - n]))
end


-- 以下为针对性优化
function bit:b_xor(a,b)
    local op1 = a
    local op2 = b
    local r = {}
 
    for i = 1, 32 do
        if op1[i] == op2[i] then
            r[i] = 0
        else
            r[i] = 1
        end
    end
    return self:b2d(r)
end
function bit:_xor_b(a,b)
    local op1 = self:d2b(a)
    local op2 = self:d2b(b)
    local r = {}
 
    for i = 1, 32 do
        if op1[i] == op2[i] then
            r[i] = 0
        else
            r[i] = 1
        end
    end
    return r
end
function bit:b_xor_b(a,b)
    local op1 = a
    local op2 = b
    local r = {}
 
    for i = 1, 32 do
        if op1[i] == op2[i] then
            r[i] = 0
        else
            r[i] = 1
        end
    end
    return r
end
function bit:b1_xor_b(a,b)
    local op1 = a
    local op2 = self:d2b(b)
    local r = {}
 
    for i = 1, 32 do
        if op1[i] == op2[i] then
            r[i] = 0
        else
            r[i] = 1
        end
    end
    return r
end
function bit:_or_b(a,b)
    local op1 = self:d2b(a)
    local op2 = self:d2b(b)
    local r = {}
    
    for i = 1, 32 do
        if op1[i] == 1 or op2[i] == 1 then
            r[i] = 1
        else
            r[i] = 0
        end
    end

    return r
end
function bit:b2_and_b(a,b)
    local op1 = self:d2b(a)
    local op2 = b
    local r = {}
    
    for i = 1, 32 do
        if op1[i] == 1 and op2[i] == 1 then
            r[i] = 1
        else
            r[i] = 0
        end
    end
    return r
end
function bit:b2_xor(a,b)
    local op1 = self:d2b(a)
    local op2 = b
    local r = {}
 
    for i = 1, 32 do
        if op1[i] == op2[i] then
            r[i] = 0
        else
            r[i] = 1
        end
    end
    return self:b2d(r)
end
function bit:_and_b(a,b)
    local op1 = self:d2b(a)
    local op2 = self:d2b(b)
    local r = {}
    
    for i = 1, 32 do
        if op1[i] == 1 and op2[i] == 1 then
            r[i] = 1
        else
            r[i] = 0
        end
    end
    return r
end

-- 数组拷贝
function TableCopy(object)
    local NewTable = {}

    for k, v in pairs(object) do
        NewTable[k] = v
    end

    return NewTable
end

-- 数组连接
function concat(a, b)
	for k, v in pairs(b) do
		table.insert(a,v)
	end
end

-- 字符串切割
function split(str, reps)
    local resultStrList = {}
    string.gsub(str,'[^'..reps..']+', function ( w )
        table.insert(resultStrList, w)
    end)
    return resultStrList
end

-- 左移
function left_nav(a, n)
	return bit:_lshift(a, n)
end

-- 左移 32位
function left(a, n)
	return bit:to32(bit:_lshift(a, n))
end

-- 左移 8位
function left_to8(a, n)
	return bit:to8(bit:_lshift(a, n))
end

-- 右移
function right(a, n)
	return bit:_rshift(a, n)
end

function SHA256_init()
	SHA256_H = {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}
	SHA256_buf = {}
	SHA256_len = 0
end

function SHA256_sigma0(x)
    return bit:b_xor(bit:_or_b(right(x, 7), left_nav(x, 25)), bit:b1_xor_b(bit:_or_b(right(x, 18), left_nav(x, 14)), right(x, 3)))
end

function SHA256_sigma1(x)
    return bit:b_xor(bit:_or_b(right(x, 17), left_nav(x, 15)), bit:b1_xor_b(bit:_or_b(right(x, 19), left_nav(x, 13)), right(x, 10)))
end

function SHA256_Sigma0(x)
    return bit:b_xor(bit:_or_b(right(x, 2), left_nav(x, 30)), bit:b_xor_b(bit:_or_b(right(x, 13), left_nav(x, 19)), bit:_or_b(right(x, 22), left_nav(x, 10))))
end

function SHA256_Sigma1(x)
    return bit:b_xor(bit:_or_b(right(x, 6), left_nav(x, 26)), bit:b_xor_b(bit:_or_b(right(x, 11), left_nav(x, 21)), bit:_or_b(right(x, 25), left_nav(x, 7))))
end

function SHA256_Ch(x, y, z)
    return bit:b2_xor(z, bit:b2_and_b(x, bit:_xor_b(y, z)))
end

function SHA256_Maj(x, y, z)
    return bit:b_xor(bit:_and_b(x, y), bit:b2_and_b(z, bit:_xor_b(x, y)))
end

function SHA256_Hash_Word_Block(H, W)
	for i = 17, 64, 1 do
		W[i] = bit:to32((SHA256_sigma1(W[i - 2]) +  W[i - 7] + SHA256_sigma0(W[i - 15]) + W[i - 16]))
	end

	local state = TableCopy(H)

	for i = 1, 64, 1 do
		local T1 = state[8] + SHA256_Sigma1(state[5]) + SHA256_Ch(state[5], state[6], state[7]) + SHA256_K[i] + W[i]
		local T2 = SHA256_Sigma0(state[1]) + SHA256_Maj(state[1], state[2], state[3])
		table.remove(state)
		table.insert(state, 1, bit:to32((T1 + T2)))
		state[5] = bit:to32((state[5] + T1))
	end

	for i = 1, 8, 1 do
		H[i] = bit:to32((H[i] + state[i]))
	end
end

function SHA256_Hash_Byte_Block(H, w)
	local W = {}
	for i = 1, 16, 1 do
		if type(w[4 * (i - 1) + 4]) == "string" then
			w[4 * (i - 1) + 4] = tonumber(w[4 * (i - 1) + 4])
		end

		W[i] = bit:_or4(left(w[4 * (i - 1) + 1], 24), left(w[4 * (i - 1) + 2], 16), left(w[4 * (i - 1) + 3], 8), w[4 * (i - 1) + 4])
	end
	SHA256_Hash_Word_Block(H, W);
end

function string_to_array(str)
	local len = string.len(str)

	local res = {}
	for i = 1, len, 1 do
		res[i] = str:byte(i)
	end
	return res
end

function SHA256_write(msg)
	if (type(msg) == "string") then
		concat(SHA256_buf, string_to_array(msg));
	else
		concat(SHA256_buf, msg);
	end

	temp = 1
	for i = 1, #SHA256_buf - 64 + 1, 64 do
		temp = temp + 64
		SHA256_Hash_Byte_Block(SHA256_H, split(table.concat(SHA256_buf, ',' ,(i - 1) + 1, (i - 1) + 64), ','))
	end
	SHA256_buf = split(table.concat(SHA256_buf, ',' ,temp), ',')
	SHA256_len = SHA256_len + #msg;
end

function SHA256_finalize(a)
	SHA256_buf[#SHA256_buf + 1] = 0x80

	if (#SHA256_buf > 64 - 8) then
		for i = #SHA256_buf + 1, 64, 1 do
			SHA256_buf[i] = 0
		end
		SHA256_Hash_Byte_Block(SHA256_H, SHA256_buf)
		SHA256_buf = {}
	end

	for i = #SHA256_buf + 1, 64 - 5, 1 do
		SHA256_buf[i] = 0
	end

	SHA256_buf[60] = bit:to8(right(SHA256_len, 29))
	SHA256_buf[61] = bit:to8(right(SHA256_len, 21))
	SHA256_buf[62] = bit:to8(right(SHA256_len, 13))
	SHA256_buf[63] = bit:to8(right(SHA256_len, 5))
	SHA256_buf[64] = left_to8(SHA256_len, 3)
	SHA256_Hash_Byte_Block(SHA256_H, SHA256_buf)

	local res = {}
	for i = 1, 8, 1 do
		res[4 * (i - 1) + 1] = right(SHA256_H[i], 24)
		res[4 * (i - 1) + 2] = bit:to8(right(SHA256_H[i], 16))
		res[4 * (i - 1) + 3] = bit:to8(right(SHA256_H[i], 8))
		res[4 * (i - 1) + 4] = bit:to8(SHA256_H[i])
	end

	SHA256_H = nil
	SHA256_buf = nil
	SHA256_len = nil
	return res
end

function HMAC_SHA256_finalize()
	local md = SHA256_finalize(1)

	for i = 1, 64, 1 do
		HMAC_SHA256_key[i] = bit:b2_xor(HMAC_SHA256_key[i], bit:_xor_b(0x36, 0x5c))
	end

	SHA256_init()
	SHA256_write(HMAC_SHA256_key)
	SHA256_write(md)

	HMAC_SHA256_key = nil
	return SHA256_finalize(2)
end

function array_to_hex_string(ary)
	local res = ""
	for i = 1, #ary, 1 do
		res = res .. SHA256_hexchars[right(ary[i], 4) + 1] .. SHA256_hexchars[bit:to4(ary[i]) + 1]
	end
	return res
end

function HMAC_SHA256_init(key)
	HMAC_SHA256_key = string_to_array(key)

	if (#HMAC_SHA256_key > 64) then
		SHA256_init();
		SHA256_write(HMAC_SHA256_key);
		HMAC_SHA256_key = SHA256_finalize();
	end

	for i = #HMAC_SHA256_key + 1, 64, 1 do
		HMAC_SHA256_key[i] = 0;
	end

	for i = 1, 64, 1 do
		HMAC_SHA256_key[i] = bit:_xor(HMAC_SHA256_key[i], 0x36);
	end
	SHA256_init();
	SHA256_write(HMAC_SHA256_key);
end

SHA256_hexchars = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}

SHA256_K = {
	0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1,
	0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
	0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
	0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
	0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
	0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
	0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b,
	0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
	0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
	0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
	0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};

function HMAC_SHA256_write(msg)
	SHA256_write(msg);
end

function HMAC_SHA256_MAC(key, msg)
	local res
	HMAC_SHA256_init(key)
	HMAC_SHA256_write(msg)
	res = HMAC_SHA256_finalize()
	return array_to_hex_string(res)
end

function fromhex(str)
    return (str:gsub('..', function (cc)
        return string.char(tonumber(cc, 16))
    end))
end

function b64encode(data)
	local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    return ((data:gsub('.', function(x) 
        local r,b='',x:byte()
        for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
        return r;
    end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
        if (#x < 6) then return '' end
        local c=0
        for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
        return b:sub(c+1,c+1)
    end)..({ '', '==', '=' })[#data%3+1])
end

function char_to_hex(c)
    return string.format("%%%02X", string.byte(c))
end

function urlencode(url)
    if url == nil then
        return
    end
    url = url:gsub("\n", "\r\n")
    url = url:gsub("([^%w _%%%-%.~])", char_to_hex)
    url = url:gsub(" ", "+")
    return url
end

function res_text(conmd, path)
    os.execute(conmd .. " > "..path)
	local result
   	pcall(function()
   		local f = io.open(path)
   		result = f:read('*a') 
   	end)
    if result then
        os.remove(path)
    end
   	return result
end

function string_ends(String,End)
	local e = string.sub(String,-string.len(End))
	if e == End then
		return true
	else
		return false
	end
end


local body_struct = {}
body_struct.resfile = '/sdcard/ret.txt'
body_struct.cjson = nil
body_struct.socket = nil
body_struct.apikey = nil
body_struct.secretkey = nil   


function ding_init()
    --if not body_struct.curl then
    --    os.execute("getprop > /sdcard/getprop.txt")
    --    local result = res_text("/sdcard/getprop.txt")
    --    result = string.match(result, "%[ro%.build%.version%.release%]: %[(.-)%]")
    --    if result == "5.1.1" then
    --        body_struct.curl = "curl -k "
    --    else
    --        body_struct.curl = "curl "
    --    end
    --end
    if not body_struct.cjson then
        body_struct.cjson = require("cjson")
    end
    if not body_struct.socket then
        body_struct.socket = require("socket")
    end
end

function mesg_body(maseg, user, bool)
    local mtable = {}
    local conten = {}
    local at = {}
    conten['content'] = maseg
    at["atMobiles"]   = {user}
    at["isAtAll"]     = bool
    mtable["text"]    = conten
    mtable["at"]      = at
    mtable["msgtype"] = "text"
    return body_struct.cjson.encode(mtable)
end

function QMPlugin.DingDing_sign(token, maseg, sign)
    ding_init()
    local mbody     =  mesg_body(maseg, "", true)

	local timestamp = string.format("%s", math.floor(body_struct.socket.gettime()*1000))
	local sign_url  = string.format("%s\n%s", timestamp, sign)
    local url_end   = urlencode(b64encode(fromhex(HMAC_SHA256_MAC(sign, sign_url))))
	local url_post  = string.format("https://oapi.dingtalk.com/robot/send?access_token=%s&sign=%s&timestamp=%s", token, url_end, timestamp)
	if string_ends(url_post,"\n") == true then
		url_post = string.sub(url_post, 1, -2)
	end

    local heads  = 'python-requests/2.25.1'
    local _type  = 'Content-Type: application/json'
	local conmd  = string.format("curl -k -X POST -d '%s' -A '%s' -H '%s' -i '%s'", mbody, heads, _type, url_post)
    local result = res_text(conmd, body_struct.resfile)
    result = string.match(result,"({.+})")
    return result
end

function QMPlugin.DingDing_key(token, maseg, key)
    ding_init()
    local mbody   =  mesg_body("["..key.."]\n"..maseg, "", true)

    local heads   = 'python-requests/2.25.1'
    local _type   = 'Content-Type: application/json'
	local url     = string.format("https://oapi.dingtalk.com/robot/send?access_token=%s", token)
	local conmd   = string.format("curl -k -i '%s' -A '%s' -H '%s' -d '%s'", url, heads, _type, mbody)
    local result  = res_text(conmd, body_struct.resfile)
    result = string.match(result,"({.+})")
    return result
end


function QMPlugin.DingDing_sign_user(token, maseg, sign, users)
    ding_init()
    local mbody     =  mesg_body(maseg, users, false)

    local timestamp = string.format("%s", math.floor(body_struct.socket.gettime()*1000))
    local sign_url  = string.format("%s\n%s", timestamp, sign)
    local url_end   = urlencode(b64encode(fromhex(HMAC_SHA256_MAC(sign, sign_url))))
    local url_post  = string.format("https://oapi.dingtalk.com/robot/send?access_token=%s&sign=%s&timestamp=%s", token, url_end, timestamp)
    if string_ends(url_post,"\n") == true then
        url_post = string.sub(url_post, 1, -2)
    end

    local heads  = 'python-requests/2.25.1'
    local _type  = 'Content-Type: application/json'
    local conmd  = string.format("curl -k -X POST -d '%s' -A '%s' -H '%s' -i '%s'", mbody, heads, _type, url_post)
    local result = res_text(conmd, body_struct.resfile)
    result = string.match(result,"({.+})")
    return result
end

function ocrgetparams(apikey, serkey)
    ding_init()
    local url    = string.format("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s", apikey, serkey)
    local conmd  = string.format("curl -k -G '%s'", url)
    local result = res_text(conmd, body_struct.resfile)
    result = body_struct.cjson.decode(result)
    return result["access_token"]
end

function QMPlugin.OcrBaiDuinit(mapikey, mserkey)
    body_struct.secretkey = mserkey
    body_struct.apikey    = mapikey
end

function QMPlugin.OcrBaiDu(...)
    local array = {...}
    local filepath
    if string_ends(array[1],".png") then
        filepath = array[1]
    else
        filepath = '/sdcard/base.png'
        LuaAuxLib.SnapShot(filepath, array[1], array[2], array[3], array[4])
    end
    local result
    pcall(function()
        local f = io.open(filepath, "rb")
        result = f:read('*a') 
    end)

    if result then
        result = urlencode(b64encode(result))
        local token = ocrgetparams(body_struct.apikey, body_struct.secretkey)
        local heads = 'python-requests/2.25.1'
        local _type = 'Content-Type:application/x-www-form-urlencoded'
        local url   = string.format("https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic?access_token=%s&aipSdk=python&aipVersion=2_2_18", token)
        local conmd = string.format("curl -k -X POST -A '%s' -H '%s' -d 'image=%s' -i '%s'", heads, _type, result, url)
        result = res_text(conmd, body_struct.resfile)
    end
    result = string.match(result,"({.+})")
    return result
end
